/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package libs.window;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.LayoutManager;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.net.URL;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.swing.JPanel;
import libs.game.Coordinates;
import libs.game.sokoban.Path;
import libs.game.sokoban.SokobanGamePlan;

/**
 *
 * @author papi
 */
public class GamePlanPanel extends JPanel {

    private static Image IMG_BOX_ON_EMPTY;
    private static Image IMG_BOX_ON_GOAL;
    private static Image IMG_GOAL;
    private static Image IMG_PERSON_ON_EMPTY_NORTH;
    private static Image IMG_PERSON_ON_EMPTY_SOUTH;
    private static Image IMG_PERSON_ON_EMPTY_EAST;
    private static Image IMG_PERSON_ON_EMPTY_WEST;
    private static Image IMG_PERSON_ON_GOAL_NORTH;
    private static Image IMG_PERSON_ON_GOAL_SOUTH;
    private static Image IMG_PERSON_ON_GOAL_EAST;
    private static Image IMG_PERSON_ON_GOAL_WEST;
    private static Image IMG_WALL;
    /**
     *          Field width.
     */
    public static final int FIELD_WIDTH = 23;
    /**
     *          Field height.
     */
    public static final int FIELD_HEIGHT = 23;
    /**
     *          The game plan which is user to paint the panel.
     */
    private SokobanGamePlan plan;
    /**
     *          Last pressed key code.
     */
    private int lastKey = -1;
    /**
     *          Coordinates of the field where the cursor is.
     */
    private Coordinates mouseOn = null;
    /**
     *          Coordinates of the fields which are marked.
     */
    private Set<Coordinates> markedFields = new HashSet<Coordinates>();
    private Path personPath;
    private Map<Coordinates, Path> boxPaths;

    public GamePlanPanel() {
        super();
        init();
    }

    public GamePlanPanel(boolean arg0) {
        super(arg0);
        init();
    }

    public GamePlanPanel(LayoutManager arg0) {
        super(arg0);
        init();
    }

    public GamePlanPanel(LayoutManager arg0, boolean arg1) {
        super(arg0, arg1);
        init();
    }

    /**
     *          It returns by cursor marked fields.
     * 
     * @return  Marked fields.
     */
    public Collection<Coordinates> getMarkedFields() {
        return Collections.unmodifiableCollection(markedFields);
    }

    private void init() {
        this.loadImages();
        this.setOpaque(false);
    }

    /**
     *          It loads images which are important to display a game panel.
     *
     * @return  TRUE if the method loaded images, FALSE if the images have been already loaded.
     */
    private boolean loadImages() {
        if (IMG_BOX_ON_EMPTY != null) {
            return false;
        }
        URL imageURL = null;

        imageURL = this.getClass().getResource("images/box_on_empty.png");
        IMG_BOX_ON_EMPTY = Toolkit.getDefaultToolkit().getImage(imageURL);
        imageURL = this.getClass().getResource("images/box_on_goal.png");
        IMG_BOX_ON_GOAL = Toolkit.getDefaultToolkit().getImage(imageURL);
        imageURL = this.getClass().getResource("images/goal.png");
        IMG_GOAL = Toolkit.getDefaultToolkit().getImage(imageURL);

        imageURL = this.getClass().getResource("images/person_on_empty_north.png");
        IMG_PERSON_ON_EMPTY_NORTH = Toolkit.getDefaultToolkit().getImage(imageURL);
        imageURL = this.getClass().getResource("images/person_on_empty_south.png");
        IMG_PERSON_ON_EMPTY_SOUTH = Toolkit.getDefaultToolkit().getImage(imageURL);
        imageURL = this.getClass().getResource("images/person_on_empty_west.png");
        IMG_PERSON_ON_EMPTY_WEST = Toolkit.getDefaultToolkit().getImage(imageURL);
        imageURL = this.getClass().getResource("images/person_on_empty_east.png");
        IMG_PERSON_ON_EMPTY_EAST = Toolkit.getDefaultToolkit().getImage(imageURL);

        imageURL = this.getClass().getResource("images/person_on_goal_north.png");
        IMG_PERSON_ON_GOAL_NORTH = Toolkit.getDefaultToolkit().getImage(imageURL);
        imageURL = this.getClass().getResource("images/person_on_goal_south.png");
        IMG_PERSON_ON_GOAL_SOUTH = Toolkit.getDefaultToolkit().getImage(imageURL);
        imageURL = this.getClass().getResource("images/person_on_goal_west.png");
        IMG_PERSON_ON_GOAL_WEST = Toolkit.getDefaultToolkit().getImage(imageURL);
        imageURL = this.getClass().getResource("images/person_on_goal_east.png");
        IMG_PERSON_ON_GOAL_EAST = Toolkit.getDefaultToolkit().getImage(imageURL);

        imageURL = this.getClass().getResource("images/wall.png");
        IMG_WALL = Toolkit.getDefaultToolkit().getImage(imageURL);
        return true;
    }

    public void markField(MouseEvent mouseEvent) {
        Coordinates coords = new Coordinates(mouseEvent.getX() / FIELD_WIDTH, mouseEvent.getY() / FIELD_HEIGHT);
        if (!this.markedFields.remove(coords)) {
            this.markedFields.add(coords);
        }
    }

    /**
     *          Sets the field where the cursor is.
     *
     * @param   mouseEvt    Event of cursor moving.
     */
    public void setCursorField(MouseEvent mouseEvent) {
        Coordinates newMouseOn = new Coordinates(mouseEvent.getX() / FIELD_WIDTH, mouseEvent.getY() / FIELD_HEIGHT);
        mouseOn = newMouseOn;
    }

    /**
     *          Unset the field where the cursor is.
     */
    public void unsetCursorField() {
        this.mouseOn = null;
    }

    /**
     *          Set fields which the person can go into.
     *
     * @param   personPath  Person path.
     */
    public void setPersonPath(Path personPath) {
        if (personPath == null) {
            throw new NullPointerException("Argument 'personPath' is null.");
        }
        this.personPath = personPath;
    }

    /**
     *          Map witch fields where the boxes can be moved into.
     *
     * @param   boxPaths  Box paths.
     */
    public void setBoxPath(Map<Coordinates, Path> boxPaths) {
        if (boxPaths == null) {
            throw new NullPointerException("Argument 'boxPaths' is null.");
        }
        this.boxPaths = boxPaths;
    }

    /**
     *          It sets a plan which is the panel paint from.
     *
     * @param   plan    Game plan.
     */
    public void setPlan(SokobanGamePlan plan) {
        this.plan = plan;
    }

    @Override
    public void paint(Graphics g) {
        if (plan == null) {
            super.paint(g);
        } else {
            this.paintGame(g);
        }
    }

    /**
     *          It paints a panel with game.
     * 
     * @param   g   Graphics.
     */
    private void paintGame(Graphics g) {
        int x = 0;
        int y = 0;
        for (char[] row : plan.getPlan()) {
            for (char field : row) {
                switch (field) {
                    default:
                        g.drawImage(IMG_BOX_ON_EMPTY, x * FIELD_WIDTH, y * FIELD_HEIGHT, this);
                        break;
                    case SokobanGamePlan.FIELD_BOX_ON_GOAL:
                        g.drawImage(IMG_BOX_ON_GOAL, x * FIELD_WIDTH, y * FIELD_HEIGHT, this);
                        break;
                    case SokobanGamePlan.FIELD_EMPTY:
                        g.setColor(Color.WHITE);
                        if ((mouseOn != null) && (mouseOn.isInRange(0, 0, plan.columns()-1, plan.rows()-1))) {
                            Coordinates currentField = new Coordinates(x, y);
                            switch (plan.getField(mouseOn)) {
                                case SokobanGamePlan.FIELD_BOX_ON_EMPTY:
                                case SokobanGamePlan.FIELD_BOX_ON_GOAL:
                                    if ((boxPaths != null) && (boxPaths.get(mouseOn).get(currentField) != null)) {
                                        g.setColor(Color.yellow);
                                    }
                                    break;
                                case SokobanGamePlan.FIELD_PERSON_ON_EMPTY:
                                case SokobanGamePlan.FIELD_PERSON_ON_GOAL:
                                    if ((personPath != null) && (personPath.get(currentField) != null)) {
                                        g.setColor(Color.yellow);
                                    }
                                    break;
                            }
                        }
                        g.fillRect(x * FIELD_WIDTH, y * FIELD_HEIGHT, FIELD_WIDTH, FIELD_HEIGHT);
                        break;
                    case SokobanGamePlan.FIELD_GOAL:
                        g.drawImage(IMG_GOAL, x * FIELD_WIDTH, y * FIELD_HEIGHT, this);
                        break;
                    case SokobanGamePlan.FIELD_PERSON_ON_EMPTY:
                        switch (this.lastKey) {
                            case KeyEvent.VK_LEFT:
                                g.drawImage(IMG_PERSON_ON_EMPTY_WEST, x * FIELD_WIDTH, y * FIELD_HEIGHT, this);
                                break;
                            case KeyEvent.VK_RIGHT:
                                g.drawImage(IMG_PERSON_ON_EMPTY_EAST, x * FIELD_WIDTH, y * FIELD_HEIGHT, this);
                                break;
                            default:
                                g.drawImage(IMG_PERSON_ON_EMPTY_NORTH, x * FIELD_WIDTH, y * FIELD_HEIGHT, this);
                                break;
                            case KeyEvent.VK_DOWN:
                                g.drawImage(IMG_PERSON_ON_EMPTY_SOUTH, x * FIELD_WIDTH, y * FIELD_HEIGHT, this);
                                break;
                        }
                        break;
                    case SokobanGamePlan.FIELD_PERSON_ON_GOAL:
                        switch (this.lastKey) {
                            case KeyEvent.VK_LEFT:
                                g.drawImage(IMG_PERSON_ON_GOAL_WEST, x * FIELD_WIDTH, y * FIELD_HEIGHT, this);
                                break;
                            case KeyEvent.VK_RIGHT:
                                g.drawImage(IMG_PERSON_ON_GOAL_EAST, x * FIELD_WIDTH, y * FIELD_HEIGHT, this);
                                break;
                            default:
                                g.drawImage(IMG_PERSON_ON_GOAL_NORTH, x * FIELD_WIDTH, y * FIELD_HEIGHT, this);
                                break;
                            case KeyEvent.VK_DOWN:
                                g.drawImage(IMG_PERSON_ON_GOAL_SOUTH, x * FIELD_WIDTH, y * FIELD_HEIGHT, this);
                                break;
                        }
                        break;
                    case SokobanGamePlan.FIELD_WALL:
                        g.drawImage(IMG_WALL, x * FIELD_WIDTH, y * FIELD_HEIGHT, this);
                        break;
                }
                x++;
            }
            y++;
            x = 0;
        }
        g.setColor(Color.RED);
        if ((mouseOn != null) && (mouseOn.getX() < this.plan.columns()) && (mouseOn.getY() < this.plan.rows())) {
            g.drawRect(mouseOn.getX() * FIELD_WIDTH, mouseOn.getY() * FIELD_HEIGHT, FIELD_WIDTH, FIELD_HEIGHT);
        }
        for (Coordinates marked : this.markedFields) {
            if ((marked.getX() < this.plan.columns()) && (marked.getY() < this.plan.rows())) {
                g.drawRect(marked.getX() * FIELD_WIDTH, marked.getY() * FIELD_HEIGHT, FIELD_WIDTH, FIELD_HEIGHT);
            }
        }
    }

    /**
     *          It repaints a panel, becuse key pressed.
     *
     * @param   evt     KeyPressed event.
     */
    public void repaint(KeyEvent evt) {
        this.repaint();
        if (evt == null) {
            throw new NullPointerException("Argument 'evt' is null.");
        }
        this.lastKey = evt.getKeyCode();
    }

    public void resetMarkedFields() {
        markedFields.clear();
    }
}

